package com.hxzy.controller.front.alipay;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.hxzy.common.enums.AckCode;
import com.hxzy.common.exception.ServiceException;
import com.hxzy.config.AlipayConfig;
import com.hxzy.entity.Merchandiser;
import com.hxzy.entity.Orders;
import com.hxzy.service.MerchandiserService;
import com.hxzy.service.OrderDetailService;
import com.hxzy.service.OrdersService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * @Controller和@RestController 区别   @Controller能够返回网页，显示网页，返回ModelAndView模型
 *                                    @RestController只能返回json
 * @Author wy
 * @Description 向支付宝提交付款申请，以及支付异步回调付款的结果
 * @Date 2023/10/7 14:23
 * @Version 1.0
 */
@Log4j2
@Controller
@RequestMapping(value = "/front/alipay")
public class AplipayController {

    @Autowired
    private OrdersService ordersService;

    @Autowired
    private MerchandiserService merchandiserService;

    @Autowired
    private OrderDetailService orderDetailService;

    /**
     * 1、向支付宝 发起支付请求
     * @param orderNumber
     */
    @GetMapping(value = "/{orderNumber}")
    public void paymentApplication(@PathVariable(value = "orderNumber") String orderNumber, HttpServletResponse response) throws AlipayApiException, IOException {
        if(StrUtil.isBlank(orderNumber)){
            throw new ServiceException(AckCode.PAY_ORDERID_NOT_NULL);
        }

        //查询订单号
       Orders db= ordersService.findByNumber(orderNumber);
       if(Objects.isNull(db)){
           throw new ServiceException(AckCode.NOT_FOUND_DATA);
       }

       //判断订单已经支付过或取消了
        if(!db.getStatus().equals("1")){
            throw new ServiceException(AckCode.PAY_ORDER_STATE_ERROR);
        }

        if(db.getCheckoutTime()==null) {
            //更新订单请求的时间
            alipayRequest(db, response);
        }else{
            //支付宝对账查询
            alipaySearch(db,response);
        }
    }


    /**
     * 支付宝对账查询
     * @param db
     * @param response
     */
    private void alipaySearch(Orders db,HttpServletResponse response) throws AlipayApiException, IOException {

        com.alipay.api.AlipayConfig alipayConfig = new com.alipay.api.AlipayConfig();
        alipayConfig.setServerUrl(AlipayConfig.URL);
        alipayConfig.setAppId(AlipayConfig.APPID);
        alipayConfig.setPrivateKey(AlipayConfig.RSA_PRIVATE_KEY);
        alipayConfig.setFormat("json");
        alipayConfig.setAlipayPublicKey(AlipayConfig.ALIPAY_PUBLIC_KEY);
        alipayConfig.setCharset("UTF-8");
        alipayConfig.setSignType("RSA2");
        AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);

        //AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);

        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel model = new AlipayTradeQueryModel();
        model.setOutTradeNo(db.getNumber());
        request.setBizModel(model);
        //查询对账号结果
        AlipayTradeQueryResponse resp = alipayClient.execute(request);

        /*
         {
            "alipay_trade_query_response": {
              "code": "10000",
              "msg": "Success",
              "buyer_logon_id": "imv***@sandbox.com",
              "buyer_pay_amount": "0.00",
              "buyer_user_id": "2088722016300001",
              "buyer_user_type": "PRIVATE",
              "invoice_amount": "0.00",
              "out_trade_no": "1710574398805708800000003",
              "point_amount": "0.00",
              "receipt_amount": "0.00",
              "send_pay_date": "2023-10-08 14:32:54",
              "total_amount": "21.00",
              "trade_no": "2023100822001400000500944853",
              "trade_status": "TRADE_SUCCESS"
            },
            "sign": "LdSvPqPbTV3uOSftUqh2bsAhSTUrSFbNoo+W3q2lo778NzgA7a1wRWd48QBqO35+Im+hjMLiIm0+NzGX9HM2B8VbTiRR6bloLGllz0mErgwIbZiPQw/YZDTWL73lpiZDlBRJv5JO0+6JxLFRNtJqXuO35SWl6NszwdEHcPKe+Qkk49m1bhR9wVdxS8CzSpkLSDI2wvmZ9dlWSTOzOEH6fdNiWmoc03ct92UBEuGQTVDjpQ28lw8iVTiGvOl2L+MrxbJ3ljmYA368uoP8YaTA/vq7jEJ2p9TvCtDCbsYe1BDEILC5lYOq4VNuBZiXTAPAOlQSuYGx3l8wEp54pFgsyw=="
          }
         */

        String msg="";
        System.out.println( resp.getBody());
        //已经交易过了
        if(resp.getCode().equals("10000")) {
            //更新数据库订单状态
            //客户实际支付金额
            String totalAmout=resp.getTotalAmount();
            //我们自己订单号
            String outTradeNo = resp.getOutTradeNo();
            //支付宝交易号
            String alipayTradeNo = resp.getTradeNo();

            Date payDate = resp.getSendPayDate();
            int count=this.ordersService.updatePayStatusByOrderId( db.getNumber() ,payDate,new BigDecimal(totalAmout),alipayTradeNo);
            //给前端输出已经支付过，不允许再支付了
            msg="订单已支付过，不允许再提交了";
        }else {
            //错误消息
             msg= resp.getMsg();
             log.error(resp.getBody());
             //更新表，check

            //我们自己订单号
            String outTradeNo = resp.getOutTradeNo();
            this.ordersService.cancelCheckOutTime(outTradeNo);
             //记录到表中，供管理员查看
        }

        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out= response.getWriter();
        String str="<script>\n" +
                "   alert('"+msg+"');\n" +
                "   window.location.href='https://tonneyyy.eicp.vip';\n" +
                "</script>";
        out.write(str);
        out.flush();
        out.close();
    }


    /** api详情参数说明  https://www.w3cschool.cn/aliminiapp/aliminiapp-qkod2f4h.html
     * 向支付宝发起支付请求
     * @param orders
     * @param response
     */
    private void alipayRequest(Orders orders,HttpServletResponse response){
        // 商户订单号，商户网站订单系统中唯一订单号，必填，最大长度是64个字符
        String out_trade_no = orders.getNumber();
        // 订单名称，必填，最大长度256个字符
        Merchandiser merchandiser = merchandiserService.getById(orders.getMerchantId());
        String subject = "向"+merchandiser.getName()+"支付费用";
        // 付款金额(保留2位小数)，必填
        BigDecimal amount = orders.getAmount();
        String total_amount= amount.setScale(2, RoundingMode.HALF_UP).toString();

        // 商品描述，可空  最大长度是128个字符
        List<String> productNameList=this.orderDetailService.findByOrderId(orders.getId());
        StringBuffer stringBuffer=new StringBuffer();
        for(int i=0;i< productNameList.size();i++){
            stringBuffer.append(productNameList.get(i));
            if(i+1!= productNameList.size()){
                stringBuffer.append(",");
            }
        }
        String body = stringBuffer.toString();
        if(body.length()>128){
            body=body.substring(0,128);
        }

        // 超时时间 可空,默认是90m，90分钟没有支付成功，支付会自动关闭订单
        String timeout_express="20m";

        // 销售产品码 必填(PC端，小程序端，H5端，andoird/IOS 手机端)
        String product_code="QUICK_WAP_WAY";

        // SDK 公共请求类，包含公共请求参数，以及封装了签名与验签，开发者无需关注签名与验签
        //调用RSA签名方式
        AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
        //H5端支付
        AlipayTradeWapPayRequest alipay_request=new AlipayTradeWapPayRequest();

        // 封装请求支付信息
        AlipayTradeWapPayModel model=new AlipayTradeWapPayModel();
        model.setOutTradeNo(out_trade_no);
        model.setSubject(subject);
        model.setTotalAmount(total_amount);
        model.setBody(body);
        model.setTimeoutExpress(timeout_express);
        model.setProductCode(product_code);
        alipay_request.setBizModel(model);
        // 设置异步通知地址
        alipay_request.setNotifyUrl(AlipayConfig.notify_url);
        // 设置同步地址
        alipay_request.setReturnUrl(AlipayConfig.return_url);
        // form表单生产
        String form = "";
        try {
            // 调用SDK生成表单
            form = client.pageExecute(alipay_request).getBody();

            //更新订单发起请求的时间
            Orders newOrders=new Orders();
            newOrders.setId(orders.getId());
            newOrders.setCheckoutTime(new Date());
            this.ordersService.updateById(newOrders);


            response.setContentType("text/html;charset=" + AlipayConfig.CHARSET);
            //直接将完整的表单html输出到页面
            response.getWriter().write(form);
            response.getWriter().flush();
            response.getWriter().close();
        } catch (AlipayApiException | IOException e) {
            // TODO Auto-generated catch block
           log.error(e);
           throw new ServiceException(AckCode.PAY_ORDER_START_ERROR);
        }
    }


    /**
     * 2、支付宝扣款以后，不管成功还是失败的回调地址
     * @param request
     * @return
     */
    @PostMapping(value = "/notify")
    public void alipayNotify(HttpServletRequest request,HttpServletResponse response) throws AlipayApiException, IOException {
        request.setCharacterEncoding("UTF-8");
        PrintWriter out=response.getWriter();

        Map<String, String> params = new HashMap<>();

        Map<String, String[]> requestParams = request.getParameterMap();
        for (String name : requestParams.keySet()) {
            params.put(name, request.getParameter(name));
        }

        log.info(requestParams.toString());

        if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
            System.out.println("=========支付宝异步回调========");
            //我们自己订单号
            String outTradeNo = params.get("out_trade_no");
            //客户实际支付金额
            String gmtPayment = params.get("gmt_payment");
            //支付宝交易号
            String alipayTradeNo = params.get("trade_no");

            // 得到 参数签名
            String sign = params.get("sign");

            //拿到内容，再一次加密与支付宝返回的sign进行比较，如果正确，支付没有问题的(数据没有修改过)
            boolean verify_result = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, "RSA2");
            // 支付宝验签
            if (verify_result) {
                // 验签通过
                System.out.println("交易名称: " + params.get("subject"));
                System.out.println("交易状态: " + params.get("trade_status"));
                System.out.println("支付宝交易凭证号: " + params.get("trade_no"));
                System.out.println("商户订单号: " + params.get("out_trade_no"));
                System.out.println("交易金额: " + params.get("total_amount"));
                System.out.println("买家在支付宝唯一id: " + params.get("buyer_id"));
                System.out.println("买家付款时间: " + params.get("gmt_payment"));
                System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));

                String orderNumber= params.get("out_trade_no");
                String payTime=params.get("gmt_payment");
                String buyerAmount=params.get("buyer_pay_amount");
                // 更新订单表  支付状态 以及支付方式  checkout_time
                Date payDate= DateUtil.parse(payTime,"yyyy-MM-dd HH:mm:ss");

                int count=this.ordersService.updatePayStatusByOrderId( orderNumber ,payDate,new BigDecimal(buyerAmount),alipayTradeNo);
                if(count==0){
                    //记录日志，记录redis中，支付成功，但是你更新数据库失败的
                    String json=JSONUtil.toJsonStr(params);
                    log.error(json+",支付成功，但更新数据库失败");
                    out.println("fail");
                }else{
                    out.println("success");
                }
            }else{
                log.error(AckCode.ORDER_PAY_CheckSignature.getMsg());
                out.println("fail");
            }
        }else{
            log.error(AckCode.ORDER_PAY_FAIL.getMsg());
            out.println("fail");
        }
    }




}
